package com.ctjsoft.fileupload.utils;

import com.ctjsoft.fileupload.entity.MinioEntity;
import com.ctjsoft.fileupload.entity.ReturnMessage;
import com.ctjsoft.fileupload.entity.TFile;
import com.ctjsoft.fileupload.entity.TMinioServer;
import com.ctjsoft.fileupload.server.TMinioServerService;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Item;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/***
 * Minio 客户端工具包，封装所有的操作类
 */
@Log4j2
public class MinioClientUtils {


    private MinioClient minioClient;

    private static int RETRY_NUM = 3;

    private static final String bucketPublicPolicy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Resource\":[\"arn:aws:s3:::test\"],\"Sid\":\"\"},{\"Action\":[\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Resource\":[\"arn:aws:s3:::test/*\"],\"Sid\":\"\"}]}";

    /*public static MinioClientUtils getInstance() {
        if (null != minioClientUtils) {
            return minioClientUtils;
        }
        synchronized (MinioClientUtils.class) {
            if (null == minioClientUtils) {
                minioClientUtils = new MinioClientUtils();
            }
        }
        return minioClientUtils;
    }
*/

    public MinioClientUtils(TMinioServer tMinioServer) {
        init(tMinioServer);
    }

    private void init(TMinioServer minioServer) {
        log.info("初始化MinioClient");
        MinioClient.Builder builder = MinioClient.builder();
        builder.endpoint(minioServer.getHost(), minioServer.getPort(), false).credentials(minioServer.getAccessKey(), minioServer.getSecretKey());
        minioClient = builder.build();
    }

    public boolean createBucketPublic(String bucketName) {
        boolean isCreated;
        try {
//            if (minioClient.bucketExists(bucketName)) {
//                isCreated = false;
//            }
            minioClient.makeBucket("buzi");
            //minioClient.setBucketPolicy(bucketName, bucketPublicPolicy);
            isCreated = true;
        } catch (Exception e) {
            isCreated = false;
            log.error("createBucketPublic", e);
            e.printStackTrace();
        }
        return isCreated;
    }

    public String uploadJpegFile(String bucketName, String minioPath, String jpgFilePath) {
        return uploadFile(bucketName, minioPath, jpgFilePath, MediaType.IMAGE_JPEG_VALUE);
    }

    public String uploadJpegStream(String bucketName, String minioPath, InputStream inputStream) {
        return uploadStream(bucketName, minioPath, inputStream, MediaType.IMAGE_JPEG_VALUE);
    }

    public String uploadStream(String bucketName, String minioFilePath, InputStream inputStream, String mediaType) {
        log.info("uploadStream for bucketName={} minioFilePath={} inputStream.getclass={}, mediaType={}", bucketName,
                minioFilePath, inputStream.getClass(), mediaType);
        if (StringUtils.isBlank(mediaType)) {
            mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
        }
        try {
            //putObjectWithRetry(bucketName, minioFilePath, inputStream, mediaType);
            return minioClient.getObjectUrl(bucketName, minioFilePath);
        } catch (Exception e) {
            log.error("uploadStream occur error:", e);
            throw new RuntimeException(e);
        }
    }

    public String uploadFile(String bucketName, String minioFilePath, String localFile, String mediaType) {
        log.info("uploadFile for bucketName={} minioFilePath={} localFile={}, mediaType={}", bucketName,
                minioFilePath, localFile, mediaType);
        if (StringUtils.isBlank(mediaType)) {
            mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
        }
        try {
            //putObjectWithRetry(bucketName, minioFilePath, localFile, mediaType);
            return minioClient.getObjectUrl(bucketName, minioFilePath);
        } catch (Exception e) {
            log.error("uploadFile occur error:", e);
            throw new RuntimeException(e);
        }
    }

    public List<MinioEntity> listFilesSwap(String bucketName, String prefix, boolean recursive) {
        log.info("list files for bucketName={} prefix={} recursive={}", bucketName, prefix, recursive);
        return swapResultToEntityList(minioClient.listObjects(bucketName, prefix, recursive));
    }

    public Iterable<Result<Item>> listFiles(String bucketName, String prefix, boolean recursive) {
        log.info("list files for bucketName={} prefix={} recursive={}", bucketName, prefix, recursive);
        return minioClient.listObjects(bucketName, prefix, recursive);
    }


    public List<MinioEntity> listFilesByBucketNameSwap(String bucketName) {
        log.info("listFilesByBucketName for bucketName={}", bucketName);
        return swapResultToEntityList(minioClient.listObjects(bucketName, null, true));
    }

    public Iterable<Result<Item>> listFilesByBucketName(String bucketName) {
        log.info("listFilesByBucketName for bucketName={}", bucketName);
        return minioClient.listObjects(bucketName, null, true);
    }

    public Iterable<Result<Item>> listFilesByBucketAndPrefix(String bucketName, String prefix) {
        log.info("listFilesByBucketAndPrefix for bucketName={} and prefix={}", bucketName, prefix);
        return minioClient.listObjects(bucketName, prefix, true);
    }

    public List<MinioEntity> listFilesByBucketAndPrefixSwap(String bucketName, String prefix) {
        log.info("listFilesByBucketAndPrefix for bucketName={} and prefix={}", bucketName, prefix);
        return swapResultToEntityList(minioClient.listObjects(bucketName, prefix, true));
    }

    private MinioEntity swapResultToEntity(Result<Item> result) {
        MinioEntity minioEntity = new MinioEntity();
        try {
            if (result.get() != null) {
                Item item = result.get();
                minioEntity.setObjectName(item.objectName());
                minioEntity.setDir(item.isDir());
                minioEntity.setEtag(item.etag());
                minioEntity.setLastModified(item.lastModified());
                minioEntity.setSize(item.size());
                minioEntity.setStorageClass(item.storageClass());
            }
        } catch (Exception e) {
            log.error("UrlUtils error, e={}", e.getMessage());
        }
        return minioEntity;
    }

    private List<MinioEntity> swapResultToEntityList(Iterable<Result<Item>> results) {
        List<MinioEntity> minioEntities = new ArrayList<>();
        for (Result<Item> result : results) {
            minioEntities.add(swapResultToEntity(result));
        }
        return minioEntities;
    }

    public ReturnMessage uploadFiles(MultipartFile[] file) {
        ReturnMessage returnMessage = new ReturnMessage();
        returnMessage.setCode("500");
        if (file == null || file.length == 0) {
            returnMessage.setMsg("上传文件不能为空");
            return returnMessage;
        }

        List<String> orgfileNameList = new ArrayList<>(file.length);

        for (MultipartFile multipartFile : file) {
            String orgfileName = multipartFile.getOriginalFilename();
            orgfileNameList.add(orgfileName);

            try {
                InputStream in = multipartFile.getInputStream();
                minioClient.putObject("MINIO_BUCKET", orgfileName, in, new PutObjectOptions(in.available(), -1));
                in.close();
            } catch (Exception e) {
                log.error(e.getMessage());
                returnMessage.setMsg("上传失败");
                return returnMessage;
            }
        }

        Map<String, Object> data = new HashMap<String, Object>();
        data.put("bucketName", "MINIO_BUCKET");
        data.put("fileName", orgfileNameList);
        returnMessage.setCode("200");
        returnMessage.setMsg("上传成功");
        returnMessage.setData(data);
        return returnMessage;
    }


    public ObjectWriteResponse putObject(MultipartFile file, String bucket) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException, RegionConflictException {
        //判断桶是否存在
        boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
        if (!bucketExists) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket("default").build());
            bucket = "default";
        }
        InputStream inputStream = file.getInputStream();
        PutObjectArgs.Builder stream = PutObjectArgs.builder()
                .contentType(file.getContentType())
                //如果文件大小未知，则将-1传递给objectSize并传递有效的partSize。
                //如果知道对象大小，则将-1传递给partSize进行自动检测；
                .stream(inputStream, file.getSize(), -1)
                .bucket(bucket)
                .object(file.getOriginalFilename()); //服务器端存储的文件名
        return minioClient.putObject(stream.build());
        //}
    }
    /*public void putObjectWithRetry(String bucketName, String objectName, InputStream stream, String contentType) throws IOException {
        int current = 0;
        boolean isSuccess = false;
        while (!isSuccess && current < RETRY_NUM) {
            try {
                minioClient.putObject(bucketName, objectName, stream, stream.available(), contentType);
                isSuccess = true;
            } catch (ErrorResponseException e) {
                log.warn("[minio] putObject stream, ErrorResponseException occur for time =" + current, e);
                current++;
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            }
        }
        if (current == RETRY_NUM) {
            log.error("[minio] putObject, backetName={}, objectName={}, failed finally!");
        }
    }*/

   /* public void putObjectWithRetry(String bucketName, String objectName, String fileName, PutObjectOptions options) {
        int current = 0;
        boolean isSuccess = false;
        while (!isSuccess && current < RETRY_NUM) {
            try {
                minioClient.putObject(bucketName, objectName, fileName, options);
                isSuccess = true;
            } catch (ErrorResponseException e) {
                current++;
                log.debug("[minio] putObject file, ErrorResponseException occur!");
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (InvalidResponseException e) {
                e.printStackTrace();
            } catch (ServerException e) {
                e.printStackTrace();
            } catch (InsufficientDataException e) {
                e.printStackTrace();
            } catch (XmlParserException e) {
                e.printStackTrace();
            } catch (InternalException e) {
                e.printStackTrace();
            } catch (InvalidBucketNameException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (current == RETRY_NUM) {
            log.error("[minio] putObject, backetName={}, objectName={}, failed finally!");
        }
    }*/

    public MinioClient getMinioClient() {
        return minioClient;
    }

    public static void main(String[] args) {
        //MinioClientUtils.getInstance().createBucketPublic("helo");
    }

}
